﻿using System;
using IGS.Core.Systems;

namespace IGS.Core.Models
{
    public class PlummerModel
    {
        public INBodySystem Generate(int n, double totalMass, double plummerRadius)
        {
            var bodies = new IInteractable[n];

            var unitMass = totalMass / n;
            var cumulativeMin = 0.0;
            var cumulativeMax = unitMass;
            var random = new Random();

            var yScale = 0.5 * 0.5 * Math.Pow(1 - 0.5 * 0.5, 3.5);

            for (int i = 0; i < n; i++)
            {
                var mass = cumulativeMin + (cumulativeMax - cumulativeMin) * random.NextDouble();
                cumulativeMin = cumulativeMax;
                cumulativeMax += unitMass;

                double x, y;
                do
                {
                    x = random.NextDouble();
                    y = yScale * random.NextDouble();
                } while (y > x * x * Math.Pow(1 - x * x, 3.5));

                var r = 1 / Math.Sqrt(Math.Pow(mass, -2.0 / 3.0) - 1.0);
                var v = x * Math.Sqrt(2 * Math.Pow(1 + r * r, -0.25));

                var position = (1.0 / plummerRadius) * NextSpherical(random, r);
                var velocity = Math.Sqrt(plummerRadius) * NextSpherical(random, v);

                bodies[i] = new Body(unitMass, position, velocity);
            }

            return new NBodySystem(bodies);
        }

        private static Vector NextSpherical(Random random, double radius)
        {
            var theta = Math.Acos(2 * random.NextDouble() - 1.0);
            var phi = 2 * Math.PI * random.NextDouble();

            return radius * new Vector(Math.Sin(theta) * Math.Cos(phi),
                                       Math.Sin(theta) * Math.Sin(phi),
                                       Math.Cos(theta));
        }

    }
}